package cn.com.scitc.licenseplateback.controller;

import biz.source_code.base64Coder.Base64Coder;
import cn.com.scitc.licenseplateback.alipay.config.AlipayConfig;
import cn.com.scitc.licenseplateback.dao.*;
import cn.com.scitc.licenseplateback.model.*;
import cn.com.scitc.licenseplateback.redis.RedisUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;


@RestController
public class Alipay {

    private Log log = LogFactory.getLog(getClass());

    /**
     * 创建订单信息
     */
    private CreateOrderInfoEntity createOrderInfoEntity;

    /**
     * 会员信息对象
     */
    @Autowired
    private MembershipInformationDao membershipInformationDao;

    /**
     * 预约入库信息
     */
    @Autowired
    private BookingWarehousingDao bookingWarehousingDao;

    /**
     * 缓存库
     */
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 调用公共方法
     */
    @Autowired
    private PublicFunction publicFunction;

    /**
     * 会员套餐 CRUD
     */
    @Autowired
    private MemberSetMealDao memberSetMealDao;

    /**
     * 会员开通充值业务
     */
    @Autowired
    private MemberPayRecoreDao memberPayRecoreDao;

    /**
     * 查询各立方库的剩余车位信息
     */
    @Autowired
    private LdleParkingDao ldleParkingDao;

    @Test
    public void test1(){
        String str = "file://#/BookingInfo";
        System.out.println(str.indexOf("#"));
        System.out.println(str.substring(0,str.indexOf("#")));
        if(!str.substring(0,str.indexOf("#")).equals("http://cube.tooo.top")){
            System.out.println("网址不对");
            System.out.println("http://cube.tooo.top"+str.substring(str.indexOf("#")-1,str.length()));
        }

    }


    /**
     * 保存用户的身份 id，供在签名认证回调中使用
     */
    private String userId;

    @PostMapping("/pay")
    public void pay(String info, HttpServletRequest request, HttpServletResponse response) throws IOException {

        JSONObject jsonObject = JSONObject.parseObject(info);
        createOrderInfoEntity = JSONObject.parseObject(jsonObject.toJSONString(), new TypeReference<CreateOrderInfoEntity>() {
        });

        // 设置同步回调地址，既用户完成了支付过后，页面返回到那儿去 -- 前端请求必需的参数
        JSONObject jsonObject1 = JSONObject.parseObject(Base64Coder.decodeString(createOrderInfoEntity.getWIDbody()));
        String url = jsonObject1.get("Return_url").toString();

        AlipayConfig.return_url = jsonObject1.get("Return_url").toString();

        //在用户进行支付页面时，获取用户的登录过期时间，进行登录过期时间的延长处理，避免发生用户
        //在支付的时候返回页面出现登录失效，以及出现的一些不可预料的错误
        String token = request.getHeader("cubelibrarytoken");
        long expire = redisUtil.getExpire(token);
        expire = expire + 900;
        redisUtil.expire(token, expire);

        //保存用户身份证号
        String tokenInfo = redisUtil.get(token).toString();
        JSONArray jsonArray = JSONArray.parseArray(tokenInfo);
        JSONObject jsonObject2 = JSONObject.parseObject(jsonArray.get(0).toString());
        this.userId = jsonObject2.get("userId").toString();

        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, "json", AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);

        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);


        //商户订单号，商户网站订单系统中唯一订单号，必填
        String out_trade_no = createOrderInfoEntity.getWIDout_trade_no();
        //付款金额，必填
        String total_amount = createOrderInfoEntity.getWIDtotal_amount();
        //订单名称，必填
        String subject = createOrderInfoEntity.getWIDsubject();
        //商品描述，可空
        String body = createOrderInfoEntity.getWIDbody();

        alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
                + "\"total_amount\":\"" + total_amount + "\","
                + "\"subject\":\"" + subject + "\","
                + "\"body\":\"" + body + "\","
                + "\"timeout_express\":\"15m\","
                + "\"quit_url\":\"http://localhost:3000/#/\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

        //请求
        String form = "";
        try {
            form = alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
        response.getWriter().write(form);//直接将完整的表单html输出到页面
        response.getWriter().flush();
        response.getWriter().close();

    }

    @RequestMapping("/notify_url")
    private String notify_url(HttpServletRequest request) {
        /**
         * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
         * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
         * 3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
         * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
         * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
         * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
         */
        Map<String, String> requestParam = getAllRequestParam(request);

        // 调用SDK验证签名
        try {
            boolean signVerified = AlipaySignature.rsaCheckV1(requestParam, AlipayConfig.ALIPAY_PUBLIC_KEY,
                    AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
            if (signVerified) {
                log.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
                if (requestParam.get("trade_status").equals("TRADE_SUCCESS") || requestParam.get("trade_status").equals("TRADE_FINISHED")) {
                    if (this.check(requestParam).equals("success")) {
                        String body = requestParam.get("body");
                        JSONObject jsonObject = JSONObject.parseObject(Base64Coder.decodeString(body));
                        if (jsonObject.get("CallID").toString().equals("1")) {
                            /**
                             * 用户预约车位业务处理
                             */
                            this.startBooking(jsonObject);
                        } else if (jsonObject.get("CallID").toString().equals("2")) {
                            /**
                             * 用户录入车辆车牌号业务处理
                             */
                            return this.startAddPlate(jsonObject);
                        }else if(jsonObject.get("CallID").toString().equals("3")){
                            /**
                             * 用户开通/充值会员业务处理
                             */
                            return this.startMemberRechargeServer(jsonObject);
                        }
                        return "success";
                    }
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return "fail";
    }


    /**
     * 会员预约入库
     *
     * @param body
     */
    @PostMapping("/memberBooking")
    private String memberBooking(String body) {
        String encode = Base64Coder.decodeString(body);
        JSONObject jsonObject = JSONObject.parseObject(encode);
        this.startBooking(jsonObject);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Base64Coder.encodeString(jsonObject.get("tradeNo").toString());
    }

    /**
     * 验证1、2、3、4
     *
     * @param params 异步返回结果
     * @throws AlipayApiException
     */
    private String check(Map<String, String> params) {
        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号
        if (!params.get("out_trade_no").equals(createOrderInfoEntity.getWIDout_trade_no())) {
            return "fail";
        }
        // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额）
        DecimalFormat df = new DecimalFormat("#########.##");
        String total_amount = df.format(Float.parseFloat(params.get("total_amount")));
        String format = df.format(Float.parseFloat(createOrderInfoEntity.getWIDtotal_amount()));
        if (!total_amount.equals(format)) {
            return "fail";
        }
        // 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
        // 第三步可根据实际情况省略
        // 4、验证app_id是否为该商户本身。
        if (!params.get("app_id").equals(AlipayConfig.APPID)) {
            return "fail";
        }
        return "success";
    }


    /**
     * 获取客户端请求参数中所有的信息
     *
     * @param request
     * @return
     */
    private Map<String, String> getAllRequestParam(final HttpServletRequest request) {
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
                //如果字段的值为空，判断若值为空，则删除这个字段>
                if (null == res.get(en) || "".equals(res.get(en))) {
                    res.remove(en);
                }
            }
        }
        return res;
    }


    /**
     * *************************************以下为主要业务处理***********************************************
     */

    /**
     * 开线程处理预约入库
     *
     * @param jsonObject 需要传入一个 JSONObject 对象，且是已经处理后的对象，若进行了编码，也需要进行编码
     */
    private void startBooking(JSONObject jsonObject) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                MembershipInformationEntity byNameAndPlateNumber = membershipInformationDao.findByNameAndPlateNumber(jsonObject.get("userName").toString(), jsonObject.get("plateNumber").toString());
                if (byNameAndPlateNumber != null) {

                    publicFunction.analyzeParkingInformation(jsonObject.get("bookingParking").toString(), "remove");

                    BookingWarehousingEntity booking = new BookingWarehousingEntity();
                    booking.setName(jsonObject.get("userName").toString());
                    booking.setIdNumber(byNameAndPlateNumber.getIdNumber());
                    booking.setLicensePlateNumber(jsonObject.get("plateNumber").toString());
                    booking.setBookingTime(jsonObject.get("bookingTime").toString());
                    booking.setMemberGrade(byNameAndPlateNumber.getGrade());
                    booking.setWaitingTime(Integer.valueOf(jsonObject.get("waitFor").toString()));
                    booking.setBookingParking(jsonObject.get("bookingParking").toString());
                    booking.setTradeNo(jsonObject.get("tradeNo").toString());
                    booking.setStatus(0);
                    booking.setAddress(jsonObject.get("cubeLibraryAddress").toString());
                    BookingWarehousingEntity save = bookingWarehousingDao.save(booking);


                    if (save == null) {
                        try {
                            throw new Exception();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).run();
    }

    @Test
    public void test(){
        String str = "[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]\n";
        JSONArray jsonArray = JSONObject.parseArray(str);
        for (int i = 0 ; i < jsonArray.size() ; i++){
            System.out.println(jsonArray.get(i));
        }

    }

    /**
     * 处理用户录入车辆车牌号
     *
     * @param jsonObject
     */
    private String startAddPlate(JSONObject jsonObject) {
        int addPlateNumber = membershipInformationDao.updateAddPlateNumber(jsonObject.get("finalPlateNumber").toString(), this.userId);
        if (addPlateNumber == 1) {
            return "success";
        } else {
            return "fail";
        }
    }

    /**
     * 会员开通充值服务
     * @param jsonObject
     */
    private String startMemberRechargeServer(JSONObject jsonObject) {
        Iterable<MemberSetMealEntity> list = memberSetMealDao.findAll();
        String unitPrice = "";
        DecimalFormat df = new DecimalFormat("######.00");
        for (MemberSetMealEntity m : list){
            if( Integer.valueOf(m.getOpenTime()) <= Integer.valueOf(jsonObject.get("openMonth").toString()) ){
                unitPrice = df.format(new Double(Double.valueOf(m.getMoney()) / Integer.valueOf(m.getOpenTime())));
            }
        }

        double shouldMoney = Math.round(Double.valueOf(unitPrice) * Integer.valueOf(jsonObject.get("openMonth").toString()));
        if(shouldMoney != Double.valueOf(jsonObject.get("actualMoney").toString())){
            return "fail";
        }
        MemberPayRecoreEntity memberPayRecoreEntity = new MemberPayRecoreEntity();
        memberPayRecoreEntity.setIdNumber(this.userId);
        memberPayRecoreEntity.setOpenTime(jsonObject.get("openTime").toString());
        memberPayRecoreEntity.setOpenMonth(jsonObject.get("openMonth").toString());
        memberPayRecoreEntity.setActuleMoney(Double.valueOf(jsonObject.get("actualMoney").toString()));
        memberPayRecoreEntity.setShouldMoney(shouldMoney);
        memberPayRecoreEntity.setSession(false);

        MemberPayRecoreEntity save = memberPayRecoreDao.save(memberPayRecoreEntity);
        if(save == null){
            return "fail";
        }
        MembershipInformationEntity membershipInformationEntity = membershipInformationDao.findbyMember(this.userId);
        membershipInformationEntity.setGrade(1);
        MembershipInformationEntity save1 = membershipInformationDao.save(membershipInformationEntity);
        if(save1 == null){
            return "fail";
        }
        return "success";
    }

}

